﻿using DrawTools.Serialize;
using DrawTools.Tools;
using DrawTools.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Xml.Serialization;

namespace DrawTools
{
    public sealed partial class DrawingCanvas : Canvas
    {
        #region 公开方法

        private static string[] stepColors = new string[]
        {
            "F44336",
            "E91E63",
            "9C27B0",
            "9C27B0",
            "3F51B5",
            "2196F3",
            "03A9F4",
            "00BCD4",
            "009688" ,
            "4CAF50",
            "8BC34A" ,
            "CDDC39" ,
            "FFEB3B" ,
            "FFC107" ,
            "FF9800" ,
            "FF5722",
            "FF5722" ,
            "9E9E9E"
        };

        private bool isBusy = false;

        private int stepIndex = 0;

        public void ToNextColor()
        {
            try
            {
                isBusy = true;
                if (stepIndex >= stepColors.Length) stepIndex = 0;
                var oxVale = stepColors[stepIndex];
                var r = Convert.ToByte(oxVale.Substring(0, 2), 16);
                var g = Convert.ToByte(oxVale.Substring(2, 2), 16);
                var b = Convert.ToByte(oxVale.Substring(4, 2), 16);
                Brush = new SolidColorBrush(Color.FromArgb(Brush.Color.A, r, g, b));
                stepIndex += 1;
            }
            finally
            {
                isBusy = false;
            }
        }

        /// <summary>
        /// 设置绘图颜色
        /// </summary>
        /// <param name="color"></param>
        public void SetPenBrush(SolidColorBrush brush)
        {
            Pen.Brush = brush;
        }

        public void Clear()
        {
            foreach (var visual in visuals)
            {
                base.RemoveLogicalChild(visual);
                base.RemoveVisualChild(visual);
            }

            this.visuals.Clear();
            this.Children.Clear();
            this.workingDrawTools.Clear();
            this.Init();
        }

        public IEnumerable<DrawGeometryBase> GetDrawGeometries()
        {
            return visuals.OfType<DrawGeometryBase>().Where(q => q.IsFinish);
        }

        private static Type[] ignoreTypes = new Type[] { typeof(PointerDrawTool), typeof(TextDrawTool), typeof(PixelRateDrawTool), typeof(ArrowDrawTool) };

        public void AddWorkingDrawTool(IDrawTool drawTool)
        {
            lock (workingDrawTools)
            {
                if (!workingDrawTools.Contains(drawTool))
                {
                    this.workingDrawTools.Add(drawTool);
                }
                if (ignoreTypes.Any(t => t == drawTool.GetType()))
                {
                    drawTool.Index = -1;
                    return;
                }
                var drawTools = visuals.Where(p => p is IDrawTool).Cast<IDrawTool>().Where(p => !ignoreTypes.Any(t => t == p.GetType()));
                if (drawTool.ShowIndex)
                {
                    drawTool.Index = drawTools.Where(p => p.ShowIndex && p.Index >= 0).Count() + 1;
                }
            }
        }

        public void DeleteWorkingDrawTool(IDrawTool drawTool)
        {
            this.workingDrawTools.Remove(drawTool);
        }

        /// <summary>
        /// 转为图片
        /// </summary>
        /// <param name="pixelWidth"></param>
        /// <param name="pixelHeight"></param>
        /// <param name="dpi"></param>
        /// <param name="image"></param>
        /// <returns></returns>
        public BitmapFrame ToBitmapFrame(Int32 pixelWidth, Int32 pixelHeight, ImageSource image)
        {
            var dpi = DpiHelper.GetDpiFromVisual(this);
            var visual = GetDrawingVisual(pixelWidth, pixelHeight, image);
            if (visual == null) return null;
            //var renderBitmap = new RenderTargetBitmap(pixelWidth, pixelHeight, dpi.DpiX, dpi.DpiY, PixelFormats.Pbgra32);
            var renderBitmap = new RenderTargetBitmap(pixelWidth, pixelHeight, 96, 96, PixelFormats.Pbgra32);
            renderBitmap.Render(visual);
            return BitmapFrame.Create(renderBitmap);
        }

        /// <summary>
        /// 打印
        /// </summary>
        /// <param name="pixelWidth"></param>
        /// <param name="pixelHeight"></param>
        /// <param name="image"></param>
        public void Print(Int32 pixelWidth, Int32 pixelHeight, ImageSource image)
        {
            var visual = GetDrawingVisual(pixelWidth, pixelHeight, image);

            if (visual == null) return;

            var printDlg = new PrintDialog();

            if (printDlg.ShowDialog() == true)
            {
                var l = Math.Max(pixelWidth, pixelHeight);
                var s = Math.Min(pixelWidth, pixelHeight);

                var sizeL = Math.Max(printDlg.PrintableAreaWidth, printDlg.PrintableAreaHeight);
                var sizeS = Math.Min(printDlg.PrintableAreaWidth, printDlg.PrintableAreaHeight);

                var zoom = Math.Min(sizeL / l, sizeS / s);
                visual.Transform = new ScaleTransform(zoom, zoom);

                if (pixelWidth != pixelHeight && (pixelWidth > pixelHeight ^ printDlg.PrintableAreaWidth > printDlg.PrintableAreaHeight))
                    printDlg.PrintTicket.PageOrientation = System.Printing.PageOrientation.Landscape;

                printDlg.PrintVisual(visual, nameof(DrawingCanvas));
            }
        }

        /// <summary>
        /// 保存画图数据到指定文件
        /// </summary>
        /// <param name="filepath"></param>
        public void SaveDrawDataToFile(String filepath)
        {
            var serializer = new DrawGeometrySerializer
            {
                Geometries = this.GetDrawGeometries().Select(q => q.ToSerializer()).ToArray()
            };

            var xml = new XmlSerializer(typeof(DrawGeometrySerializer));

            using (var fs = new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                xml.Serialize(fs, serializer);
            }
        }

        /// <summary>
        /// 获取画图数据(Xml格式)
        /// </summary>
        /// <returns></returns>
        public string GetDrawDataToXml()
        {
            var serializer = new DrawGeometrySerializer
            {
                Geometries = this.GetDrawGeometries().Where(p => p.DrawingToolType != DrawToolType.Check).Select(q => q.ToSerializer()).ToArray()
            };

            var xml = new XmlSerializer(typeof(DrawGeometrySerializer));

            using (var fs = new MemoryStream())
            {
                xml.Serialize(fs, serializer);
                var data = System.Text.Encoding.UTF8.GetString(fs.ToArray());
                return data.TrimStart();
            }
        }

        /// <summary>
        /// 从指定文件中加载画图数据
        /// </summary>
        /// <param name="filepath"></param>
        public void LoadDrawDataFromFile(String filepath)
        {
            var xml = new XmlSerializer(typeof(DrawGeometrySerializer));

            using (var fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                if (fs.Length == 0) return;
                var serializer = (DrawGeometrySerializer)xml.Deserialize(fs);
                this.Clear();
                foreach (var draw in serializer.Geometries)
                {
                    this.AddVisual(draw.Deserialize(this));
                }
            }
            //RefreshTogetherText();
        }

        /// <summary>
        /// 设置画图数据
        /// </summary>
        /// <param name="xmlData"></param>
        public void SetDrawDataFromXml(string xmlData)
        {
            var xml = new XmlSerializer(typeof(DrawGeometrySerializer));
            var data = System.Text.Encoding.UTF8.GetBytes(xmlData);
            using (var fs = new MemoryStream(data))
            {
                var serializer = (DrawGeometrySerializer)xml.Deserialize(fs);
                this.Clear();
                foreach (var draw in serializer.Geometries)
                {
                    this.AddVisual(draw.Deserialize(this));
                }
            }
        }

        private TextBlock togetherText = null;

        public void Init()
        {
            if (DrawerConfig.Instance.TextShowTogether && togetherText == null)
            {
                togetherText = new TextBlock();
                togetherText.RenderTransform = new TranslateTransform();
                togetherText.Text = "";
                togetherText.FontSize = this.FontSize;
                togetherText.Foreground = Pen.Brush;
                togetherText.IsEnabled = true;
                togetherText.Foreground = Pen.Brush;
                togetherText.Cursor = Cursors.Hand;
                togetherText.Background = Brushes.Transparent;
                togetherText.PreviewMouseLeftButtonDown += MouseLeftButtonDown;
                togetherText.MouseMove += MouseMove;
                togetherText.PreviewMouseLeftButtonUp += MouseLeftButtonUp;
                Panel.SetZIndex(togetherText, int.MaxValue);
                Canvas.SetLeft(togetherText, 5);
                Canvas.SetRight(togetherText, 5);
            }
            if (togetherText != null)
            {
                if (!this.Children.Contains(togetherText)) this.Children.Add(togetherText);
                togetherText.Text = String.Empty;
            }
            //DrawingToolType = DrawToolType.Pointer;
        }

        public void RefreshTogetherText()
        {
            if (!DrawerConfig.Instance.TextShowTogether || togetherText == null || visuals.Count == 0) return;
            togetherText.Text = string.Empty;
            togetherText.FontSize = DrawerConfig.Instance.FontSize;
            togetherText.Foreground = DrawerConfig.Instance.DefaultBrush;// Pen.Brush;
            foreach (var item in visuals.Where(p => p is IDrawTool).Cast<IDrawTool>())
            {
                if (!string.IsNullOrEmpty(item.Text) && item.Index > 0 && item.ShowIndex)
                {
                    if (!string.IsNullOrEmpty(togetherText.Text))
                    {
                        togetherText.Text += Environment.NewLine;
                    }
                    if (item.Index > 0)
                    {
                        togetherText.Text += item.Index.ToString().PadLeft(2) + ")---- " + item.Text;
                    }
                }
            }
            togetherText.UpdateLayout();
        }

        /// <summary>
        /// 添加集中文本显示内容
        /// </summary>
        /// <param name="lineTxt"></param>
        public void AddTogetherText(string lineTxt)
        {
            if (!DrawerConfig.Instance.TextShowTogether || togetherText == null) return;
            if (!string.IsNullOrEmpty(togetherText.Text))
            {
                togetherText.Text += Environment.NewLine;
            }
            togetherText.Text += lineTxt;
            togetherText.UpdateLayout();
        }

        #endregion 公开方法

        #region 私有方法

        #region drag

        private bool isDragging = false;
        private Point startPosition;

        private void MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            isDragging = true;
            var draggableElement = sender as UIElement;
            var clickPosition = e.GetPosition(this);

            var transform = draggableElement.RenderTransform as TranslateTransform;
            startPosition.X = clickPosition.X - transform.X;    //注意减号
            startPosition.Y = clickPosition.Y - transform.Y;

            draggableElement.CaptureMouse();
            e.Handled = true;
        }

        private void MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            isDragging = false;
            var draggableElement = sender as UIElement;
            draggableElement.ReleaseMouseCapture();
            e.Handled = true;
        }

        private void MouseMove(object sender, MouseEventArgs e)
        {
            var draggableElement = sender as UIElement;
            if (isDragging)
            {
                e.Handled = true;
                var currentPosition = e.GetPosition(this);
                var transform = draggableElement.RenderTransform as TranslateTransform;
                transform.X = currentPosition.X - startPosition.X;
                transform.Y = currentPosition.Y - startPosition.Y;
            }
        }

        #endregion drag

        private IDrawTool CreateDrawingTool()
        {
            switch (DrawingToolType)
            {
                case DrawToolType.Pointer:
                    return new PointerDrawTool(this);

                case DrawToolType.Pen:
                    return new PenDrawTool(this);

                case DrawToolType.Eraser:
                    return new EraserDrawTool(this);

                case DrawToolType.Line:
                    return new LineDrawTool(this);

                case DrawToolType.Arrow:
                    return new ArrowDrawTool(this);

                case DrawToolType.Ranging:
                    return new RangingDrawTool(this);

                case DrawToolType.Length:
                    return new LengthDrawTool(this);

                case DrawToolType.Rectangle:
                    return new RectangleDrawTool(this);

                case DrawToolType.Ellipse:
                    return new EllipseDrawTool(this);

                case DrawToolType.Angle:
                    return new AngleDrawTool(this);

                case DrawToolType.Polyline:
                    return new PolylineDrawTool(this);

                case DrawToolType.Curve:
                    return new CurveDrawTool(this);

                case DrawToolType.Polygon:
                    return new PolygonDrawTool(this);

                case DrawToolType.ClosedCurve:
                    return new ClosedCurveDrawTool(this);

                case DrawToolType.ClosedLine:
                    return new ClosedLineDrawTool(this);

                case DrawToolType.AreaClosedLine:
                    return new AreaClosedLineDrawTool(this);

                case DrawToolType.Area:
                    return new AreaDrawTool(this);

                case DrawToolType.Text:
                    return new TextDrawTool(this);

                case DrawToolType.Check:
                    return new PixelRateDrawTool(this);

                default:
                    throw new ArgumentOutOfRangeException("不支持画图工具" + DrawingToolType);
            }
        }

        private void UpdateCursor()
        {
            switch (this.DrawingToolType)
            {
                case DrawToolType.Pointer:
                    this.OriginalCursor = Cursors.Arrow;
                    this.squareCursor = false;
                    break;

                case DrawToolType.Text:
                    this.OriginalCursor = Cursors.IBeam;
                    this.squareCursor = false;
                    break;

                default:
                    if ((squareCursor && cursorLength == StrokeThickness && cursorZoom == Zoom) || Double.IsNaN(StrokeThickness) || Double.IsNaN(Zoom))
                        break;
                    var w = (UInt32)Math.Max(5, StrokeThickness * Zoom);// (UInt32)Math.Max(1, StrokeThickness * Zoom);
                    var h = (UInt32)Math.Max(5, StrokeThickness * Zoom);// (UInt32)Math.Max(1, StrokeThickness * Zoom);
                    var border = (UInt32)Math.Max(1, 2 * Math.Min(1, Zoom));
                    this.OriginalCursor = DrawCursors.CreateBmpCursor(w, h, border, new SolidColorBrush(Colors.Blue));
                    this.squareCursor = true;
                    this.cursorLength = StrokeThickness;
                    this.cursorZoom = Zoom;
                    break;
            }
        }

        private DrawingVisual GetDrawingVisual(Int32 pixelWidth, Int32 pixelHeight, ImageSource image = null)
        {
            var drawGeometries = this.GetDrawGeometries();
            var root = new DrawingVisual();
            var dc = root.RenderOpen();
            if (image != null)
            {
                dc.DrawImage(image, new Rect(new Size(pixelWidth, pixelHeight)));// * dpi.Px2WpfX, pixelHeight * dpi.Px2WpfY)));
            }
            if (drawGeometries.Any())
            {
                foreach (var draw in drawGeometries)
                {
                    dc.DrawDrawing(draw.Drawing);
                }
            }
            if (togetherText != null && !string.IsNullOrEmpty(togetherText.Text))
            {
                var formattedText = GetFormattedText(togetherText.Text);
                var transform = (TranslateTransform)togetherText.RenderTransform;
                dc.DrawText(formattedText, new Point(togetherText.Margin.Left + transform.X, togetherText.Margin.Top + transform.Y));
            }
            dc.Close();
            return root;
        }

        #endregion 私有方法
    }
}